#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include "sf_json_str.h"
#include "sf_json_reader.h"

static const char * parse_json_key(JSON_DATA * item, const char * str);
static const char * parse_json_value(JSON_DATA * item, const char * str);
static const char * parse_json_array(JSON_DATA * item, const char * str);
static const char * parse_json_object(JSON_DATA *item, const char * str);
static const char * parse_json_number(JSON_DATA *item, const char * str);
static const char * parse_json_string(JSON_DATA *item, const char * str);

const char * parse_json_key(JSON_DATA * item, const char * str) {
	char *out = NULL;
	const char *ptr = str;
	const char *key_start = NULL;
	const char *key_end = NULL;
	size_t len = 0;

	if (str == NULL || strlen(str) <= 2) {
		return NULL;
	}
	while (ptr) {
		if (*ptr == '\"') {
			if (key_start) {
				key_end = ptr;
				ptr ++;
				break;
			} else {
				key_start = ptr;
			}
		}
		ptr ++;
	}
	if (key_start && key_end) {
		len = key_end - key_start - 1;
		item->key = (char *)json_malloc(len + 1, 0);
		json_strncpy(item->key, key_start + 1, len);
	}
	return ptr;
}

const char * parse_json_value(JSON_DATA * item, const char * str) {
	if (!str) {
		return NULL;
	}
	if (*str == '\"') {
		return parse_json_string(item, str);
	} else if (*str == '-' || isdigit(*str)) {
		return parse_json_number(item, str);
	} else if (*str == '[') {
		return parse_json_array(item, str);
	} else if (*str == '{') {
		return parse_json_object(item, str);
	}
	return NULL;
}

const char * parse_json_array(JSON_DATA * item, const char * str) {
	JSON_DATA * child = NULL;

	if (*str != '[') {
		return NULL;
	}

	child = new_json_data();
	if (!child)
		return NULL;

	item->type = EDATA_ARRAY;
	item->child= child;
	str = parse_json_value(child, str + 1);
	if (!str) {
		return NULL;
	}
	
	while (*str == ',') {
		JSON_DATA *new_item = new_json_data();
		if (!new_item) {
			return NULL;
		}
		child->next = new_item;
		new_item->pre = child;
		child = new_item;

		str = parse_json_value(child, str + 1);
		if (NULL == str) {
			return NULL;
		}
	}
	
	if (*str == ']') {
		return str + 1;
	}
	return NULL;
}

const char * parse_json_object(JSON_DATA *item, const char * str) {
	JSON_DATA * child = NULL;
	if (*str != '{') {
		return NULL;
	}

	item->type = EDATA_OBJECT;
	str = str + 1;
	if (*str == '}') { // Empty array.
		return str + 1;
	}

	if (item->key == NULL && item->next == NULL && item->child == NULL) {
		child = item;
	} else {
		child = new_json_data();
		item->child = child;
	}

	if (!child) {
		return NULL;
	}
	str = parse_json_key(child, str);
	if (*str != ':') {
		return NULL;
	}
	str = parse_json_value(child, str + 1);
	if (!str)
		return NULL;

	while (*str == ',') {
		JSON_DATA * new_item = new_json_data();
		if (!new_item) {
			return NULL;
		}
		child->next = new_item;
		new_item->pre = child;
		child = new_item;

		str = parse_json_key(child, str + 1);
		if (*str != ':') {
			return NULL;
		}
		str = parse_json_value(child, str + 1);
		if (!str) {
			return 0;
		}
	}

	if (*str == '}') {
		return str + 1;
	}
	return NULL;
}

const char *parse_json_number(JSON_DATA *item, const char * str) {
	double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;

	/* Could use sscanf for this? */
	if (*str == '-') 
		sign = -1, str++;	/* Has sign? */
	if (*str == '0') 
		str++;			/* is zero */
	if (*str >= '1' && *str <= '9')
	{
		do	n = (n * 10.0) + (*str++ - '0');	
		while (*str >= '0' && *str <= '9');	/* Number? */
	}
	if (*str == '.' && str[1] >= '0' && str[1] <= '9') 
	{
		str++;		
		do	n = (n * 10.0) + (*str++ - '0'), scale--; 
		while (*str >= '0' && *str <= '9');
	}	
	/* Fractional part? */
	if (*str == 'e' || *str == 'E')		/* Exponent? */
	{	
		str++;
		if (*str == '+') 
			str++;	
		else if (*str == '-') 
			signsubscale = -1, str++;		/* With sign? */
		while (*str >= '0' && *str <= '9') 
			subscale = (subscale * 10) + (*str++ - '0');	/* Number? */
	}

	n=sign * n * pow(10.0, (scale + subscale * signsubscale));	/* number = +/- number.fraction * 10^+/- exponent */

	item->value = (char *)json_malloc(30, 0);
	sprintf(item->value, "%f", n);
	item->type = EDATA_NUMBER;
	return str;
}

const char *parse_json_string(JSON_DATA *item, const char * str) {
	char *out = NULL;
	const char *ptr = str;
	const char *value_start = NULL;
	const char *value_end = NULL;
	size_t len = 0;

	if (str == NULL || strlen(str) <= 2) {
		return NULL;
	}
	while (ptr) {
		if (*ptr == '\"') {
			if (value_start) {
				value_end = ptr;
				ptr ++;
				break;
			} else {
				value_start = ptr;
			}
		}
		ptr ++;
	}
	if (value_start && value_end) {
		len = value_end - value_start - 1;
		item->type = EDATA_STRING;
		item->value = (char *)json_malloc(len + 1, 0);
		json_strncpy(item->value, value_start + 1, len);
	}
	return ptr;
}

char * json_strcpy(char *dst, const char *src)
{
	if (!dst || !src)
		return NULL;

	if (*src == '\0')
	{
		*dst = '\0';
		return dst;
	}

	while (*src)
	{
		*dst = *src;
		++dst;
		++src;
	}

	return dst;
}

char * json_strncpy(char *dst, const char *src, size_t size)
{
	char *p = dst;
	if (!dst || !src)
		return NULL;

	if (size < 1)
		return dst;

	while (size -- > 0)
	{
		*dst = *src;
		++dst;
		++src;
	}

	return p;
}

void * json_memcpy(void *dst, const void *src, size_t size)
{
	char *dst_n = (char *)dst;
	char *src_n = (char *)src;
	char *dend = src_n + size;

	if (!dst || !src)
		return NULL;

	if (size < 1)
		return dst;

	while (dst_n < dend)
	{
		*dst_n = *src_n;
		++dst_n;
		++src_n;
	}

	return dst;
}


void json_memset(void *dst, int ch, size_t size)
{
	char *p = dst;
	if (size < 1)
		return ;

	while (size-- > 0)
	{
		*p = ch;
		++p;
	}
}

void *json_malloc(const size_t size, int ch) {
	void *dst = malloc(size);
	json_memset(dst, ch, size);
	return dst;
}

void *json_realloc(void *dst, const size_t new_size) {
	dst = realloc(dst, new_size);

	return dst;
}
